package dalgo.reglas;

import java.util.LinkedList;

import javax.rmi.CORBA.Util;

import uniandes.cupi2.collections.arbol.arbolBinario.ArbolBinario;
import uniandes.cupi2.collections.arbol.arbolBinario.NodoArbolBinario;
import dalgo.main.Estado;
import dalgo.main.Main;
import dalgo.utils.Utils;

public class Atraccion extends Regla {

	@Override
	public LinkedList<Estado> aplicarRegla(ArbolBinario<String> arbol,
			int idPadre) {

		LinkedList<Estado> resultado = new LinkedList<Estado>();

		LinkedList<Estado> ret = new LinkedList<Estado>();
		NodoArbolBinario<String> hijoDer = arbol.darRaiz().darDerecho();
		NodoArbolBinario<String> hijoIzq = arbol.darRaiz().darIzquierdo();

		String reDer = hijoDer.buscar(Main.getVariable());
		String reIzq = hijoIzq.buscar(Main.getVariable());

		if ((reDer != null) && (reIzq != null)) {

			int cantVariablesDer = Utils.contarCantVariables(hijoDer);
			int cantVariablesIzq = Utils.contarCantVariables(hijoIzq);

			if (Utils.esUnario(hijoDer.darElemento())
					|| hijoDer.darElemento().equals(Main.getVariable())) {

				ret.add(operadorPonerSola(hijoDer.darElemento(),
						arbol.darRaiz(), idPadre));

			} else if (Utils.esUnario(hijoIzq.darElemento())
					|| hijoIzq.darElemento().equals(Main.getVariable())) {
				ret.add(operadorPonerSola(hijoDer.darElemento(),
						arbol.darRaiz(), idPadre));

			} else if (cantVariablesDer > cantVariablesIzq) {

				Estado e = operadorPonerSola(hijoIzq.darElemento(),
						arbol.darRaiz(), idPadre);

				ret.add(e);
				// LinkedList<Estado> eAlterno = generarAlternos(
				// Utils.copiarArbol(e.getArbol()), idPadre);
				// if (!eAlterno.isEmpty())
				// ret.add(e);
			} else {
				Estado e = operadorPonerSola(hijoDer.darElemento(),
						arbol.darRaiz(), idPadre);

				ret.add(e);
				// LinkedList<Estado> eAlterno = generarAlternos(
				// Utils.copiarArbol(e.getArbol()), idPadre);
				// if (!eAlterno.isEmpty())
				// ret.add(e);
			}

		} else {

			ArbolBinario<String> arbolD = Utils.copiarArbol(arbol);

			NodoArbolBinario<String> nodoD = verificarSimplificarDivision(arbolD);
			if (nodoD != null) {

				simplificarDivision(nodoD);
				Estado e = new Estado(arbolD, idPadre,
						"Simplificacion de division");

				ret.add(e);
			}

		}
		return ret;
	}

	private void simplificarDivision(NodoArbolBinario<String> padre) {

		NodoArbolBinario<String> hIzq = padre.darIzquierdo();
		NodoArbolBinario<String> hDer = padre.darDerecho();
		NodoArbolBinario<String> nodo;
		if (hIzq.darElemento().equals(DIVISION)) {

			hIzq = hIzq.darIzquierdo();
			hDer = hIzq.darDerecho();
			if (hIzq.darElemento().equals(MULTIPLICACION)) {

				if (hIzq.darIzquierdo().darElemento()
						.equals(hDer.darElemento())) {

					nodo = hIzq.darDerecho();
					padre.encadenarIzquierdo(nodo);
					
				} else if (hIzq.darDerecho().darElemento()
						.equals(hDer.darElemento())) {

					nodo = hIzq.darIzquierdo();
					padre.encadenarIzquierdo(nodo);

				}

			} else if (hDer.darElemento().equals(MULTIPLICACION)) {

				if (hDer.darIzquierdo().darElemento()
						.equals(hIzq.darElemento())) {

					nodo = hDer.darDerecho();
					padre.encadenarIzquierdo(nodo);
				} else if (hDer.darDerecho().equals(hIzq.darElemento())) {
					nodo = hDer.darIzquierdo();
					padre.encadenarIzquierdo(nodo);
				}

			}
		} else if (hDer.darElemento().equals(DIVISION)){

			hIzq = hDer.darIzquierdo();
			hDer = hDer.darDerecho();

			if (hDer.darElemento().equals(MULTIPLICACION)) {

				if (hIzq.darIzquierdo().darElemento()
						.equals(hDer.darElemento())) {

					nodo = hIzq.darDerecho();
					padre.encadenarDerecho(nodo);

				} else if (hIzq.darDerecho().darElemento()
						.equals(hDer.darElemento())) {

					nodo = hIzq.darIzquierdo();
					padre.encadenarDerecho(nodo);

				}

			} else if (hDer.darElemento().equals(MULTIPLICACION)) {

				if (hDer.darIzquierdo().darElemento()
						.equals(hIzq.darElemento())) {

					nodo = hDer.darDerecho();
					padre.encadenarDerecho(nodo);
				} else if (hDer.darDerecho().equals(hIzq.darElemento())) {
					nodo = hDer.darIzquierdo();
					padre.encadenarDerecho(nodo);
				}

			}
		}

	}

	private NodoArbolBinario<String> verificarSimplificarDivision(
			ArbolBinario<String> arbolD) {

		NodoArbolBinario<String> raiz = arbolD.darRaiz();

		return verificarDivisionRecursivo(raiz);

	}


	private NodoArbolBinario<String> verificarDivisionRecursivo(
			NodoArbolBinario<String> raiz) {

		NodoArbolBinario<String> resp = null;

		if (raiz.darDerecho() != null) {
			if (verificarDivision(raiz.darDerecho()) == true) {

				resp = raiz;
			} else if (verificarDivision(raiz.darIzquierdo()) == true) {

				resp = raiz;
			} else
				resp = verificarDivisionRecursivo(raiz.darDerecho());
		}
		if (raiz.darIzquierdo() != null && resp == null) {
			if (verificarDivision(raiz.darDerecho()) == true) {

				resp = raiz;
			} else if (verificarDivision(raiz.darIzquierdo()) == true) {

				resp = raiz;
			}

			else
				resp = verificarDivisionRecursivo(raiz.darIzquierdo());
		}

		return resp;
	}

	private boolean verificarDivision(NodoArbolBinario<String> nodo) {

		boolean resp = false;
		NodoArbolBinario<String> hIzq = nodo.darIzquierdo();
		NodoArbolBinario<String> hDer = nodo.darDerecho();

		if (nodo.darElemento().equals(DIVISION)) {

			if (hIzq.darElemento().equals(MULTIPLICACION)) {
				// TODO
				// CAMBIAR EL EQUALS POR UN COMPARE
				if (hIzq.darIzquierdo().darElemento()
						.equals(hDer.darElemento())) {
					resp = true;
				} else if (hIzq.darDerecho().equals(hDer.darElemento())) {
					resp = true;
				}

			} else if (hDer.darElemento().equals(MULTIPLICACION)) {

				if (hIzq.darIzquierdo().darElemento()
						.equals(hIzq.darElemento())) {
					resp = true;
				} else if (hIzq.darDerecho().darElemento()
						.equals(hIzq.darDerecho())) {
					resp = true;
				}

			}
		}

		return resp;

	}

	/**
	 * 
	 */
	private Estado ponerSolaUnario(NodoArbolBinario<String> raiz, int idPadre) {

		NodoArbolBinario<String> hIzq = raiz.darIzquierdo();
		NodoArbolBinario<String> hDer = raiz.darDerecho();
		if (Utils.esUnario(hDer.darElemento())
				|| hDer.darElemento().equals(Main.getVariable())) {

			raiz.encadenarDerecho(new NodoArbolBinario<String>("0"));

			NodoArbolBinario<String> nodo = new NodoArbolBinario<String>("+");
			raiz.encadenarIzquierdo(nodo);
			nodo.encadenarIzquierdo(hIzq);
			nodo.encadenarDerecho(hDer);

		} else {

			raiz.encadenarIzquierdo(new NodoArbolBinario<String>("0"));

			NodoArbolBinario<String> nodo = new NodoArbolBinario<String>("+");
			raiz.encadenarDerecho(nodo);
			nodo.encadenarIzquierdo(hIzq);
			nodo.encadenarDerecho(hDer);

		}

		ArbolBinario<String> arbol = new ArbolBinario<String>();

		arbol.definirRaiz(raiz);
		Estado e = new Estado(arbol, idPadre, "Atraccion- Rama unaria");

		return e;

	}

	/**
	 * Transforma ecuaciones del tipo x + a = b pre: entra como raiz el =
	 */
	private Estado sumaPonerSola(NodoArbolBinario<String> raiz, int idPadre) {
		NodoArbolBinario<String> hIzq = raiz.darIzquierdo();
		NodoArbolBinario<String> hDer = raiz.darDerecho();

		if (hIzq.darElemento().equals(SUMA)) {
			NodoArbolBinario<String> val1 = hIzq.darIzquierdo();
			NodoArbolBinario<String> val2 = hIzq.darDerecho();
			String reDer = val1.buscar(Main.getVariable());
			if (reDer == null) {
				raiz.encadenarIzquierdo(val1);
				NodoArbolBinario<String> menos = new NodoArbolBinario<String>(
						RESTA);
				raiz.encadenarDerecho(menos);
				menos.encadenarDerecho(val2);
				menos.encadenarIzquierdo(hDer);
			} else {
				raiz.encadenarIzquierdo(val2);
				NodoArbolBinario<String> menos = new NodoArbolBinario<String>(
						RESTA);
				raiz.encadenarDerecho(menos);
				menos.encadenarDerecho(val1);
				menos.encadenarIzquierdo(hDer);
			}
		} else {
			NodoArbolBinario<String> val1 = hDer.darIzquierdo();
			NodoArbolBinario<String> val2 = hDer.darDerecho();
			String reDer = val1.buscar(Main.getVariable());
			if (reDer == null) {
				raiz.encadenarDerecho(val1);
				NodoArbolBinario<String> menos = new NodoArbolBinario<String>(
						RESTA);
				raiz.encadenarIzquierdo(menos);
				menos.encadenarIzquierdo(hIzq);
				menos.encadenarDerecho(val2);
			} else {
				raiz.encadenarDerecho(val2);
				NodoArbolBinario<String> menos = new NodoArbolBinario<String>(
						RESTA);
				raiz.encadenarIzquierdo(menos);
				menos.encadenarIzquierdo(hIzq);
				menos.encadenarDerecho(val1);
			}
		}
		ArbolBinario<String> arbol = new ArbolBinario<String>();

		arbol.definirRaiz(raiz);
		Estado e = new Estado(arbol, idPadre, "Atraccion- Suma");

		return e;
	}

	/**
	 * Transforma ecuaciones del tipo x + a = b pre: entra como raiz el =
	 */
	private Estado restaPonerSola(NodoArbolBinario<String> raiz, int idPadre) {
		NodoArbolBinario<String> hIzq = raiz.darIzquierdo();
		NodoArbolBinario<String> hDer = raiz.darDerecho();

		if (hIzq.darElemento().equals(RESTA)) {
			NodoArbolBinario<String> val1 = hIzq.darIzquierdo();
			NodoArbolBinario<String> val2 = hIzq.darDerecho();
			String reDer = val1.buscar(Main.getVariable());
			if (reDer == null) {
				raiz.encadenarIzquierdo(val1);
				NodoArbolBinario<String> mas = new NodoArbolBinario<String>(
						SUMA);
				raiz.encadenarDerecho(mas);
				mas.encadenarDerecho(val2);
				mas.encadenarIzquierdo(hDer);

			} else {
				raiz.encadenarIzquierdo(val2);
				NodoArbolBinario<String> menos = new NodoArbolBinario<String>(
						SUMA);
				raiz.encadenarDerecho(menos);
				menos.encadenarDerecho(val1);
				menos.encadenarIzquierdo(hDer);
			}
		} else {
			NodoArbolBinario<String> val1 = hDer.darIzquierdo();
			NodoArbolBinario<String> val2 = hDer.darDerecho();
			String reDer = val1.buscar(Main.getVariable());
			if (reDer == null) {
				raiz.encadenarDerecho(val1);
				NodoArbolBinario<String> menos = new NodoArbolBinario<String>(
						SUMA);
				raiz.encadenarIzquierdo(menos);
				menos.encadenarIzquierdo(hIzq);
				menos.encadenarDerecho(val2);
			} else {
				raiz.encadenarDerecho(val2);
				NodoArbolBinario<String> menos = new NodoArbolBinario<String>(
						SUMA);
				raiz.encadenarIzquierdo(menos);
				menos.encadenarIzquierdo(hIzq);
				menos.encadenarDerecho(val1);
			}
		}
		ArbolBinario<String> arbol = new ArbolBinario<String>();

		arbol.definirRaiz(raiz);
		Estado e = new Estado(arbol, idPadre, "Atraccion- Resta");

		return e;
	}

	/**
	 * Transforma ecuaciones del tipo x * a = b pre: entra como raiz el =
	 */
	private Estado multiplicacionPonerSola(NodoArbolBinario<String> raiz,
			int idPadre) {
		NodoArbolBinario<String> hIzq = raiz.darIzquierdo();
		NodoArbolBinario<String> hDer = raiz.darDerecho();

		int cantVariablesDer = Utils.contarCantVariables(hDer);
		int cantVariablesIzq = Utils.contarCantVariables(hIzq);

		if (hIzq.darElemento().equals(MULTIPLICACION)
				&& (cantVariablesIzq < cantVariablesDer)) {
			NodoArbolBinario<String> val1 = hIzq.darIzquierdo();
			NodoArbolBinario<String> val2 = hIzq.darDerecho();
			if (!val1.darElemento().equals(Main.getVariable())) {
				raiz.encadenarIzquierdo(val1);
				NodoArbolBinario<String> mas = new NodoArbolBinario<String>(
						DIVISION);
				raiz.encadenarDerecho(mas);
				mas.encadenarDerecho(val2);
				mas.encadenarIzquierdo(hDer);

			} else {
				raiz.encadenarIzquierdo(val2);
				NodoArbolBinario<String> menos = new NodoArbolBinario<String>(
						DIVISION);
				raiz.encadenarDerecho(menos);
				menos.encadenarDerecho(val1);
				menos.encadenarIzquierdo(hDer);
			}
		} else {
			NodoArbolBinario<String> val1 = hDer.darIzquierdo();
			NodoArbolBinario<String> val2 = hDer.darDerecho();
			if (!val1.darElemento().equals(Main.getVariable())) {
				raiz.encadenarDerecho(val1);
				NodoArbolBinario<String> menos = new NodoArbolBinario<String>(
						DIVISION);
				raiz.encadenarIzquierdo(menos);
				menos.encadenarIzquierdo(hIzq);
				menos.encadenarDerecho(val2);
			} else {
				raiz.encadenarDerecho(val2);
				NodoArbolBinario<String> menos = new NodoArbolBinario<String>(
						DIVISION);
				raiz.encadenarIzquierdo(menos);
				menos.encadenarIzquierdo(hIzq);
				menos.encadenarDerecho(val1);
			}
		}
		ArbolBinario<String> arbol = new ArbolBinario<String>();

		arbol.definirRaiz(raiz);
		Estado e = new Estado(arbol, idPadre, "Atraccion- Multiplicacion");

		return e;
	}

	/**
	 * Transforma ecuaciones del tipo x / a = b pre: entra como raiz el =
	 */
	private Estado divisionPonerSola(NodoArbolBinario<String> raiz, int idPadre) {
		NodoArbolBinario<String> hIzq = raiz.darIzquierdo();
		NodoArbolBinario<String> hDer = raiz.darDerecho();

		if (hIzq.darElemento().equals(DIVISION)) {
			NodoArbolBinario<String> val1 = hIzq.darIzquierdo();
			NodoArbolBinario<String> val2 = hIzq.darDerecho();
			if (!val1.darElemento().equals(Main.getVariable())) {
				raiz.encadenarIzquierdo(val1);
				NodoArbolBinario<String> mas = new NodoArbolBinario<String>(
						MULTIPLICACION);
				raiz.encadenarDerecho(mas);
				mas.encadenarDerecho(val2);
				mas.encadenarIzquierdo(hDer);

			} else {
				raiz.encadenarIzquierdo(val2);
				NodoArbolBinario<String> menos = new NodoArbolBinario<String>(
						MULTIPLICACION);
				raiz.encadenarDerecho(menos);
				menos.encadenarDerecho(val1);
				menos.encadenarIzquierdo(hDer);
			}
		} else {
			NodoArbolBinario<String> val1 = hDer.darIzquierdo();
			NodoArbolBinario<String> val2 = hDer.darDerecho();
			if (!val1.darElemento().equals(Main.getVariable())) {
				raiz.encadenarDerecho(val1);
				NodoArbolBinario<String> menos = new NodoArbolBinario<String>(
						MULTIPLICACION);
				raiz.encadenarIzquierdo(menos);
				menos.encadenarIzquierdo(hIzq);
				menos.encadenarDerecho(val2);
			} else {
				raiz.encadenarDerecho(val2);
				NodoArbolBinario<String> menos = new NodoArbolBinario<String>(
						MULTIPLICACION);
				raiz.encadenarIzquierdo(menos);
				menos.encadenarIzquierdo(hIzq);
				menos.encadenarDerecho(val1);
			}
		}
		ArbolBinario<String> arbol = new ArbolBinario<String>();

		arbol.definirRaiz(raiz);
		Estado e = new Estado(arbol, idPadre, "Atraccion- Division");

		return e;
	}

	private Estado operadorPonerSola(String op, NodoArbolBinario<String> raiz,
			int idPadre) {
		if (op.equals(LOG)) {
			return ponerSolaUnario(raiz, idPadre);
		} else if (op.equals(INVLOG)) {
			return ponerSolaUnario(raiz, idPadre);
		} else if (op.equals(RAIZ)) {
			return ponerSolaUnario(raiz, idPadre);
		} else if (op.equals(CUADRADO)) {
			return ponerSolaUnario(raiz, idPadre);
		} else if (op.equals(SUMA)) {
			return sumaPonerSola(raiz, idPadre);
		} else if (op.equals(RESTA)) {
			return restaPonerSola(raiz, idPadre);
		} else if (op.equals(MULTIPLICACION)) {
			return multiplicacionPonerSola(raiz, idPadre);
		} else {
			return divisionPonerSola(raiz, idPadre);
		}
	}
}
